﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace JustCleanIt.Common
{
    /// <summary>
    /// Class for Serialize and Deserialize the object.
    /// </summary>
    public static class ObjectFormatter
    {
        #region Fields

        /// <summary>
        /// Tracing instance
        /// </summary>
        private static readonly LogManager myLogger = LogManager.GetLogger(typeof(ObjectFormatter));
        #endregion

        #region Private Methods
        /// <summary>
        /// To convert a Byte Array of Unicode values (UTF-8 encoded) to a complete String.
        /// </summary>
        /// <param name="characters">Unicode Byte Array to be converted to String</param>
        /// <returns>String converted from Unicode Byte Array</returns>
        private static string UTF8ByteArrayToString(byte[] characters)
        {
            myLogger.LogInfo("UTF8ByteArrayToString: Enter");

            UTF8Encoding encoding = new UTF8Encoding();
            string constructedString = encoding.GetString(characters);

            myLogger.LogInfo("UTF8ByteArrayToString: Exit");

            return constructedString;
        }

        /// <summary>
        /// Converts the String to UTF8 Byte array and is used in De serialization
        /// </summary>
        /// <param name="pXmlString"></param>
        /// <returns></returns>
        private static Byte[] StringToUTF8ByteArray(string pXmlString)
        {
            myLogger.LogInfo("StringToUTF8ByteArray: Enter");

            UTF8Encoding encoding = new UTF8Encoding();
            byte[] byteArray = encoding.GetBytes(pXmlString);

            myLogger.LogInfo("StringToUTF8ByteArray: Exit");

            return byteArray;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Serialize an object into an XML string
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool SerializeObject<T>(T obj, string path)
        {
            myLogger.LogInfo("SerializeObject<T>: Enter");
            bool result = false;
            try
            {
                FileInfo pathInfo = new FileInfo(path);
                if (!pathInfo.Directory.Exists)
                {
                    pathInfo.Directory.Create();
                }

                XmlSerializer xs = new XmlSerializer(typeof(T));
                XmlTextWriter xmlTextWriter = new XmlTextWriter(path, Encoding.UTF8);
                xmlTextWriter.Formatting = Formatting.Indented;
                xs.Serialize(xmlTextWriter, obj);
                xmlTextWriter.Close();
                result = true;
            }
            catch (Exception ex)
            {
                string error = string.Format("Exception while SerializeObject to the file {0}. (Returning empty string). ", path);
                myLogger.LogWarning(error + ex.ToString(),
                                    EventIds.ObjectSerializationDeserializationError);
            }
            myLogger.LogInfo("SerializeObject<T>: Exit");
            return result;
        }

        /// <summary>
        /// Serialize an object into an XML string
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string SerializeObject<T>(T obj)
        {
            myLogger.LogInfo("SerializeObject<T>: Enter");
            string xmlContent = string.Empty;
            try
            {
                MemoryStream stream = new MemoryStream();
                XmlSerializer xs = new XmlSerializer(typeof(T));
                xs.Serialize(stream, obj);
                xmlContent = UTF8ByteArrayToString(stream.ToArray());
            }
            catch (Exception ex)
            {
                myLogger.LogWarning("Exception while SerializeObject (Returning empty string). " + ex.ToString(), EventIds.ObjectSerializationDeserializationError);
            }
            myLogger.LogInfo("SerializeObject<T>: Exit");
            return xmlContent;
        }

        /// <summary>
        /// Reconstruct an object from an XML string
        /// </summary>
        /// <param name="xmlContent"></param>
        /// <returns></returns>
        public static T DeserializeObject<T>(string xmlContent)
        {
            myLogger.LogInfo("DeserializeObject<T>: Enter");

            try
            {
                XmlSerializer xs = new XmlSerializer(typeof(T));
                MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(xmlContent));

                myLogger.LogInfo("DeserializeObject<T>: Exit");
                return (T)xs.Deserialize(memoryStream);
            }
            catch (Exception ex)
            {
                myLogger.LogWarning("Exception while DeserializeObject (Returning default(T) ). " + ex.ToString(), EventIds.ObjectSerializationDeserializationError);
                myLogger.LogInfo("DeserializeObject<T>: Exit");
                return default(T);
            }
        }

        /// <summary>
        /// Serialize an object as XML into byte array encoded as UTF-8
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns>serialized object as byte array (in UTF8Encoding)</returns>
        public static byte[] SerializeObjectAsBytes<T>(T obj)
        {
            myLogger.LogInfo("SerializeObjectAsBytes<T>: Enter");
            byte[] serializedData = null;
            try
            {
                MemoryStream stream = new MemoryStream();
                XmlSerializer xs = new XmlSerializer(typeof(T));
                xs.Serialize(stream, obj);
                serializedData = stream.ToArray();
            }
            catch (Exception ex)
            {
                myLogger.LogWarning("Exception while SerializeObject (Returning empty string). " + ex.ToString(), EventIds.ObjectSerializationDeserializationError);
            }
            myLogger.LogInfo("SerializeObjectAsBytes<T>: Exit");
            return serializedData;
        }

        /// <summary>
        /// Reconstruct an object from serialized XML data represented in byte array encoded as UTF-8
        /// </summary>
        /// <param name="serializedData"></param>
        /// <returns></returns>
        public static T DeserializeObjectFromBytes<T>(byte[] serializedData)
        {
            myLogger.LogInfo("DeserializeObjectFromBytes<T>: Enter");
            try
            {
                XmlSerializer xs = new XmlSerializer(typeof(T));
                MemoryStream memoryStream = new MemoryStream(serializedData);

                myLogger.LogInfo("DeserializeObjectFromBytes<T>: Exit");
                return (T)xs.Deserialize(memoryStream);
            }
            catch (Exception ex)
            {
                myLogger.LogWarning("Exception while DeserializeObject (Returning default(T) ). " + ex.ToString(), EventIds.ObjectSerializationDeserializationError);
                myLogger.LogInfo("DeserializeObjectFromBytes<T>: Exit");
                return default(T);
            }
        }

        /// <summary>
        /// Reconstruct an object from a given xml file
        /// </summary>
        /// <param name="xmlFileUri">Absolute path of the xml file</param>
        /// <returns></returns>
        public static T DeserializeObject<T>(Uri xmlFileUri)
        {
            myLogger.LogInfo("DeserializeObject<T>: Enter");
            try
            {
                XmlSerializer xs = new XmlSerializer(typeof(T));
                FileStream stream = File.OpenRead(xmlFileUri.OriginalString);
                T retObj = (T)xs.Deserialize(stream);
                stream.Close();
                myLogger.LogInfo("DeserializeObject<T>: Exit");
                return retObj;
            }
            catch (Exception ex)
            {
                myLogger.LogWarning("Exception while DeserializeObject (Returning default(T) ). " + ex.ToString(), EventIds.ObjectSerializationDeserializationError);
                myLogger.LogInfo("DeserializeObject<T>: Exit");
                return default(T);
            }
        }
        #endregion
    }
}
